local stormlib = require 'ffi.stormlib'
local w2l = require 'w3x2lni'
local ev = require 'ev'
local mapdump = require 'mapdump'
local lniloader = require 'fix-wtg.lniloader'

local mt = {}
mt.__index = mt

function mt:process_create(command_line, current_dir)
    if self.h then
        self.h:kill()
        self.h = nil
    end

    local p = sys.process()
    p:hide_window()
    if not p:create(nil, command_line, current_dir) then
        log.error('Executed failed: ', command_line)
        return
    end
    log.trace('Executed: ', command_line)
    self.h = p
end

function mt:__gc()
    if self.h then
        self.h:kill()
        self.h = nil
    end
end

local ydhost = setmetatable({}, mt)

local function getplayernum(mappath)
    local ok, result = pcall(function()
        local map = stormlib.open(mappath, true)
        if not map then
            return 0
        end
        local w3i = map:load_file('war3map.w3i')
        local tbl = w2l:read_w3i(w3i)
        local n = 0
        if tbl['选项']['自定义玩家分组'] == 0 then
            n = tbl['玩家']['玩家数量']
        else
            for i = 1, tbl['玩家']['玩家数量'] do
                if tbl['玩家' .. i]['类型'] == 1 then
                    n = n + 1
                end
            end
        end
        map:close()
        return n
    end)
    if ok then return result end
    return 0
end


local function single_test(commandline, mappath)
    return '"' .. (fs.war3_path() / 'war3.exe'):string() .. '" -loadfile "' .. mappath:string() .. '"' .. commandline
end

local function path_sub(a, b)
    local i = a
    local r = fs.path('')
    while i ~= '' and i ~= b do
        r = i:filename() / r
        i = i:parent_path()
    end
    return r
end

local function host_copy_dll(curdir)
    pcall(fs.copy_file, fs.ydwe_path() / 'bin' / 'vcruntime140.dll', curdir / 'vcruntime140.dll', true)
    pcall(fs.copy_file, fs.ydwe_path() / 'bin' / 'msvcp140.dll', curdir / 'msvcp140.dll', true)
end

local function get_war3_version()
    if global_config["War3Patch"]["Option"] == "1" then
        if global_config["MapSave"]["Option"] == "1" then
            return sys.war3_version {
                major    = 1,
                minor    = 20,
                revision = 4,
                build    = 6074,
            }
        else
            return sys.war3_version {
                major    = 1,
                minor    = 24,
                revision = 4,
                build    = 6387,
            }
        end
    elseif global_config["War3Patch"]["Option"] == "2" then
        return sys.war3_version {
            file = fs.ydwe_path() / 'share' / 'patch' / global_config["War3Patch"]["DirName"] / 'Game.dll'
        }
    end
    return war3_version
end

local function host_save_config(curdir, mappath, autostart)
    local ver = get_war3_version()
    local jasspath
    if ver:is_new() then
        jasspath = fs.ydwe_path() / "jass" / "system" / "ht"
    else
        jasspath = fs.ydwe_path() / "jass" / "system" / "rb"
    end
    local of = io.open(curdir / 'map.cfg', 'wb')
    local ok, e = pcall(mapdump, mappath, jasspath,
        function(s)
            of:write(s .. '\n')
        end
    )
    of:close()
    if not ok then
        log.error('')
    end
    local tbl = {
        lan_war3version = ver.minor,
        bot_defaultgamename = mappath:filename():string(),
        bot_autostart = autostart,
        bot_mappath = path_sub(mappath, fs.war3_path()):string(),
        bot_mapcfgpath = 'map.cfg',
    }
    local str = ''
    for k, v in pairs(tbl) do
        str = str .. tostring(k) .. ' = ' .. tostring(v) .. '\n'
    end
    io.save(curdir / 'ydhost.cfg', str)
end

local function host_test(commandline, mappath)
    local host_test = tonumber(global_config["HostTest"]["Option"])
    local curdir = fs.ydwe_path() / 'plugin' / 'ydhost'
    host_copy_dll(curdir)
    host_save_config(curdir, mappath, host_test + 1)
    ydhost:process_create(curdir / 'ydhost.exe', curdir)
    local cmd = '"' .. (fs.war3_path() / 'war3.exe'):string() .. '"' .. commandline .. ' -auto'
    if host_test == 0 then
        return cmd, 1
    end
    return cmd, getplayernum(mappath)
end

local current_map_path
ev.on('打开地图', function(map_path)
    current_map_path = fs.path(map_path)
end)

-- 本函数在测试地图时使用
-- event_data - 事件参数，table，包含以下值
--	map_path - 保存的地图路径，字符串类型
--	application_name - 应用程序名，字符串对象
--	command_line - 命令行，字符串对象
-- 返回：非负表示成功，负数表示失败。如成功且无特殊情况原则上应该返回0
function event.EVENT_TEST_MAP(event_data)
    log.debug("********************* on test start *********************")

    -- 刷新配置数据
    global_config_reload()

    -- 获取当前测试的地图名
    local mappath = fs.path(event_data.map_path)
    -- 未保存过的新建地图
    if not current_map_path then
        current_map_path = mappath
    end
    log.info('Current map path ' .. current_map_path:string())
    log.debug("Testing " .. mappath:string())
    log.debug("Testing " .. event_data.command_line)
    log.debug("Testing " .. current_map_path:string())

    local useSlk = global_config["MapTest"]["EnableMapSlk"] ~= '0'
    if lniloader.is_lni(current_map_path) then
        if not lniloader.make_test(current_map_path, mappath, useSlk) then
            log.error('Pack map failed', current_map_path:string())
            return -1
        end
        log.info('Packed map success, use slk', useSlk)
    elseif useSlk then
        if not lniloader.make_test(current_map_path, mappath, true) then
            log.error('Pack map failed', current_map_path:string())
            return -1
        end
        log.info('Packed map success, use slk', useSlk)
    end

    -- 附加命令行
    local commandline = ""
    local n = 0

    -- 是否OpenGL方式？
    if global_config["MapTest"]["LaunchRenderingEngine"] == "OpenGL" then
        commandline = commandline .. " -opengl"
    end

    -- 是否窗口方式？
    if global_config["MapTest"]["LaunchWindowed"] ~= "0" then
        commandline = commandline .. " -window"
    end

    commandline = commandline .. ' -kkwe "' .. fs.ydwe_path():parent_path():string() .. '"'

    if global_config["MapTest"]["EnableMlScript"] ~= "0" then
        local folder = global_config["MapTest"]["MlScriptFolder"]
        if folder and folder:trim() ~= "" then
            commandline = commandline .. ' -scriptrunnerfolder "' .. folder .. '"'
        end
    end

    log.debug("Testing " .. tostring(global_config["MapTest"]["EnableHost"]))
    if global_config["MapTest"]["EnableHost"] == "1" then
        commandline, n = host_test(commandline, mappath)
    else
        commandline, n = single_test(commandline, mappath), 1
    end

    ev.emit('测试地图', current_map_path:string())

    local result = false
    -- 启动魔兽开始测试...
    for i = 1, n do
        local war3_helper_dll = fs.ydwe_path() / "plugin" / "warcraft3" / "yd_loader.dll"
        result = sys.spawn_inject(event_data.application_name, commandline, nil, war3_helper_dll)
    end

    if temp_path then
        fs.remove(temp_path)
    end

    log.debug("********************* on test end *********************")
    if result then return 0 else return -1 end
end
